﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Weazel.Badger.Vhdl.Types
{
  public class UnaryTypeResolver
  {
    private bool resultIsValid;

    private UnaryTypeMap[] maps;
    private TypeSet child;

    private GezelType childGezelType;
    private GezelType resultGezelType;

    private Dictionary<TypeId, UnaryConversionResult> results;

    public UnaryTypeResolver(
      TypeSet child, 
      GezelType childGezelType,
      GezelType resultGezelType,
      UnaryTypeMap[] maps
    )
    {
      this.child = child;
      this.maps = maps;
      this.childGezelType = childGezelType;
      this.resultGezelType = resultGezelType;
      this.results = new Dictionary<TypeId, UnaryConversionResult>();

      this.resultIsValid = false;
    }

    public UnaryConversionResult GetConversions(TypeId type)
    {
      if (!resultIsValid)
        throw new Exception("Result is not valid. Must call 'ComputePossibleResultTypes'");

      if (!results.ContainsKey(type))
        throw new Exception("Specified type is not present in results set");

      return results[type];
    }

    private TypeSet createTypeSetResult()
    {
      TypeSet result = new TypeSet();

      foreach (TypeId type in results.Keys)
      {
        if (result.Contains(type))
          throw new Exception("Type already present in set");

        result.Add(Types.CreateType(type, resultGezelType.Width, false));
      }

      return result;
    }


    public TypeSet ComputePossibleResultTypes()
    {
      if (resultIsValid)
        return createTypeSetResult();

      results = new Dictionary<TypeId, UnaryConversionResult>();

      foreach (UnaryTypeMap map in maps)
      {
        TypeConversionResult cr = 
          TypeConverter.GetConversion(
            child, 
            Types.CreateType(
              map.Child, 
              resultGezelType.Width, 
              false
            ), 
            childGezelType,
            resultGezelType,
            false
          );

        // cr = null  -> no conversion possible
        if (cr != null)
        {
          if (results.ContainsKey(map.Result))
          {
            // target type already in result collection. Replace if this conversion 
            // is better (requires fewer conversions).
            UnaryConversionResult r = results[map.Result];

            if (r.ChildConversions.Depth > cr.Depth)
            {
              r.ChildConversions = cr;
              r.Map = map;
            }
          }
          else
          {
            // not in result collection? add it
            results.Add(map.Result, new UnaryConversionResult(cr, map));
          }
        }
      }

      resultIsValid = true;

      // finally from dictionary to TypeSet. Check for duplicates.
      return createTypeSetResult();
    }
  }
}
